package org.tinaelle.hibernate.audit;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Iterator;

import org.apache.log4j.Logger;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.PropertyNotFoundException;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.property.Getter;
import org.hibernate.type.OneToOneType;
import org.springframework.orm.hibernate3.LocalSessionFactoryBean;
import org.tinaelle.BeanLocator;

/**
 * DOCUMENT ME!
 * 
 * @author $author$
 * @version $Revision$
 */
public class ParseHibEntity {
	private final Logger	   log	             = Logger.getLogger( this.getClass() );
	private PersistentClass	   pc	             = null;
	private final Object	   entity;
	private final String[]	   properties;
	public static final String	DATE_FORMAT	     = "yyyy-MM-dd";
	public static final String	DATE_TIME_FORMAT	= "yyyy-MM-dd hh:mm:ss a";

	/**
	 * Creates a new instance of ParseHibEntity
	 */
	public ParseHibEntity(final Object entity, final String[] properties) {
		this.pc = extractPersistentClass( entity.getClass().getName() );
		this.entity = entity;
		this.properties = properties;
	} // end ParseHibEntity()

	private PersistentClass extractPersistentClass(final String entityName) {
		PersistentClass pc = null;
		LocalSessionFactoryBean sessionFactory = null;

		try {
			sessionFactory = ( LocalSessionFactoryBean ) BeanLocator.instance().getBean( "&sessionFactory" );
			pc = ( sessionFactory ).getConfiguration().getClassMapping( entityName );
		} // end try
		catch ( final NullPointerException npe ) {
			log.error( "NullPointerException - make sure you get the right session factory " + npe.getMessage(), npe );
		} // end catch

		return pc;
	} // end extractPersistentClass()

	/**
	 * DOCUMENT ME!
	 * 
	 * @return DOCUMENT ME!
	 */
	public Integer extractPrimaryKeyValue() {
		// Get pk column and value
		Integer pkValue = null;

		try {
			pkValue = ( Integer ) pc.getIdentifierProperty().getGetter( entity.getClass() ).get( entity );
		} // end try
		catch ( final PropertyNotFoundException pnfe ) {
			log.info( "PropertyNotFoundException " + pnfe.getMessage() );
		} // end catch
		catch ( final MappingException me ) {
			log.info( "MappingException " + me.getMessage() );
		} // end catch
		catch ( final HibernateException he ) {
			log.info( "HibernateException " + he.getMessage() );
		} // end catch
		catch ( final NullPointerException npe ) {
			log.info( "NullPointerException " + npe.getMessage() );
		} // end catch

		return pkValue;
	} // end extractPrimaryKeyValue()

	/**
	 * DOCUMENT ME!
	 * 
	 * @return DOCUMENT ME!
	 */
	public String extractPrimaryKeyColumn() {
		String pkColumn = null;

		try {
			pkColumn = ( ( Column ) pc.getIdentifierProperty().getColumnIterator().next() ).getName();
		} // end try
		catch ( final NullPointerException npe ) {
			log.info( "NullPointerException " + npe.getMessage() );
		} // end catch

		return pkColumn;
	} // end extractPrimaryKeyColumn()

	/**
	 * DOCUMENT ME!
	 * 
	 * @param localPC
	 *            DOCUMENT ME!
	 * 
	 * @return DOCUMENT ME!
	 */
	public String extractPrimaryKeyColumn(final PersistentClass localPC) {
		String pkColumn = null;

		try {
			pkColumn = ( ( Column ) localPC.getIdentifierProperty().getColumnIterator().next() ).getName();
		} // end try
		catch ( final NullPointerException npe ) {
			log.info( "NullPointerException " + npe.getMessage() );
		} // end catch

		return pkColumn;
	} // end extractPrimaryKeyColumn()

	/**
	 * DOCUMENT ME!
	 * 
	 * @param localPC
	 *            DOCUMENT ME!
	 * @param localEntity
	 *            DOCUMENT ME!
	 * 
	 * @return DOCUMENT ME!
	 */
	public String extractPrimaryKeyValue(final PersistentClass localPC, final Object localEntity) {
		// Get pk column and value
		String pkValue = null;

		try {
			pkValue = localPC.getIdentifierProperty().getGetter( localEntity.getClass() ).get( localEntity ).toString();
		} // end try
		catch ( final PropertyNotFoundException pnfe ) {
			log.error( "PropertyNotFoundException " + pnfe.getMessage(), pnfe );
		} // end catch
		catch ( final MappingException me ) {
			log.error( "MappingException " + me.getMessage(), me );
		} // end catch
		catch ( final HibernateException he ) {
			log.error( "HibernateException " + he.getMessage(), he );
		} // end catch
		catch ( final NullPointerException npe ) {
			log.error( "NullPointerException " + npe.getMessage(), npe );
		} // end catch

		return pkValue;
	} // end extractPrimaryKeyValue()

	/**
	 * DOCUMENT ME!
	 * 
	 * @return DOCUMENT ME!
	 */
	@SuppressWarnings("unchecked")
	public String extractColumnValues() {
		final StringBuffer sb = new StringBuffer();

		// add the pk column and value
		sb.append( extractPrimaryKeyColumn() + " = " + extractPrimaryKeyValue() + " | " );

		// System.out.println("pc.getProperty (scale) " + pc.getProperty
		// ("scale"));

		// loop through all the other properties and get what you need
		for ( final String p : properties ) {
			final Property pr = pc.getProperty( p );

			// make sure that this is not a collection and not a one to one as
			// these values are not part of the table
			if ( !pr.getType().isCollectionType() && !( pr.getType() instanceof OneToOneType ) ) {
				// make sure that the values are persistent values and not a
				// forumla value
				if ( pr.isInsertable() || pr.isUpdateable() ) {
					int scale = 2;

					// get the getter for the entity
					final Getter getter = pr.getGetter( entity.getClass() );

					// get column value
					Object columnValue = getter.get( entity );

					// get column name
					for ( final Iterator it3 = pr.getColumnIterator(); it3.hasNext(); ) {
						final Column column = ( Column ) it3.next();
						sb.append( column.getName() );
						scale = column.getScale();
					} // end for

					sb.append( " = " );

					// check what kind of type of value this is, it if it an
					// association then get the forign key value from the
					// associated entity
					if ( columnValue != null ) {
						if ( !pr.getType().isAssociationType() ) {
							// if bigD set Scale
							if ( columnValue instanceof BigDecimal ) {
								columnValue = ( ( BigDecimal ) columnValue ).setScale( scale,
								        BigDecimal.ROUND_HALF_DOWN );
							} // end if
							else if ( columnValue instanceof java.util.Date ) {
								SimpleDateFormat sdf = null;

								if ( columnValue instanceof java.sql.Timestamp ) {
									sdf = new SimpleDateFormat( DATE_TIME_FORMAT );
								} // end if
								else {
									sdf = new SimpleDateFormat( DATE_FORMAT );
								} // end else

								columnValue = sdf.format( columnValue );
							} // end else if
							else if ( pr.getType().getName().equalsIgnoreCase(
							        "org.springframework.orm.hibernate3.support.ClobStringType" ) ) {
								columnValue = "Clob Value";
							} // end else if

							sb.append( columnValue );
						} // end if
						else {
							// since it's an association we know that column
							// value is an object
							final String associatedEntityName = pr.getType().getName();

							// associatedEntityName =
							// ((EntityType)pr.getType()).getAssociatedEntityName
							// ();
							final PersistentClass localPC = extractPersistentClass( associatedEntityName );
							final String fkValue = extractPrimaryKeyValue( localPC, columnValue );
							sb.append( fkValue );
						} // end else
					} // end if

					sb.append( " | " );
				} // end if
			} // end if
		} // end for

		return sb.toString();
	} // end extractColumnValues()

	/**
	 * DOCUMENT ME!
	 * 
	 * @return DOCUMENT ME!
	 */
	public String extractTableName() {
		try {
			return pc.getTable().getName();
		} // end try
		catch ( final NullPointerException npe ) {
			log.error( "NullPointerException - table is null " + npe.getMessage(), npe );

			return "";
		} // end catch
	} // end extractTableName()
} // end ParseHibEntity
